﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using SqlSugar;
using EFCore.Core.Enums;
using System.Linq;

namespace EFCore.DBBase.SqlSugar
{
    /// <summary>
    /// 数据库操作类
    /// </summary>
    public class BaseDbContext
    {
        private static string ConnectionString = string.Empty;
        private static string ip = string.Empty;
        private static string port = string.Empty;
        private static string user = string.Empty;
        private static string pass = string.Empty;
        private static string database = string.Empty;
        private static string dbType = string.Empty;
        private static int dbTypeInt = 1;

        //private Database Connection = null;

        public BaseDbContext(string conn = "", 
            string jsonConfigFileName = "appsettings.json", 
            DatabaseType databaseType = DatabaseType.SqlServer)
        {
            var config = new ConfigurationBuilder()
                             .SetBasePath(Directory.GetCurrentDirectory())
                             .AddJsonFile(jsonConfigFileName, optional: true)
                             .Build();
            if (string.IsNullOrEmpty(conn))
            {
                conn = config.GetSection("Connections:DefaultConnect").Value;
            }
            if (string.IsNullOrEmpty(ip)|| 
                    string.IsNullOrEmpty(port) || 
                    string.IsNullOrEmpty(user) || 
                    string.IsNullOrEmpty(pass) ||
                     string.IsNullOrEmpty(database))
            {
                ip = config.GetSection("ConnectionSettings:ServerIP").Value;
                port = config.GetSection("ConnectionSettings:Port").Value;
                user = config.GetSection("ConnectionSettings:User").Value;
                pass = config.GetSection("ConnectionSettings:Password").Value;
                database = config.GetSection("ConnectionSettings:DataBase").Value;
                dbType = config.GetSection("Connections:DbType").Value;
                dbTypeInt = config.GetSection("Connections:DbTypeInt").Value.ObjToInt();
            }
            ConnectionString = conn;
        }

        /// <summary>
        /// 转换数据库类型
        /// </summary>
        /// <param name="databaseType">数据库类型</param>
        /// <returns></returns>
        public static DatabaseType GetDataBaseType(string databaseType)
        {
            DatabaseType returnValue = DatabaseType.SqlServer;
            foreach (DatabaseType dbType in Enum.GetValues(typeof(DatabaseType)))
            {
                if (dbType.ToString().Equals(databaseType, StringComparison.OrdinalIgnoreCase))
                {
                    returnValue = dbType;
                    break;
                }
            }
            return returnValue;
        }

        /// <summary>
        /// 注意当前方法的类不能是静态的 public static class这么写是错误的
        /// </summary>
        public static SqlSugarClient Db
        {
            get
            {
                return InitDataBase( new List<string> { ConnectionString });
            }
        }
        /// <summary>
        ///  获得SqlSugarClient
        /// </summary>
        /// <param name="serverIp">服务器IP或文件路径</param>
        /// <param name="user">用户名</param>
        /// <param name="pass">密码</param>
        /// <param name="dataBase">数据库</param>
        public static SqlSugarClient GetIntance()
        {
            var listConn = new List<string>();
            switch ((DbType)dbTypeInt)
            {
                case DbType.SqlServer:
                    listConn.Add($"server={ip};user id={user};password={pass};persistsecurityinfo=True;database={database}");
                    break;
                case DbType.MySql:
                    listConn.Add($"Server={ip};Database={database};Uid={user};Pwd={pass};");
                    break;
                case DbType.Oracle:
                    listConn.Add($"Server=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST={ip})(PORT=1521)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME={database})));" +
                        $"User Id={user};Password={pass};Persist Security Info=True;Enlist=true;Max Pool Size=300;Min Pool Size=0;Connection Lifetime=300");
                    break;
                case DbType.PostgreSQL:
                    listConn.Add($"PORT={port};DATABASE={database};HOST={ip};PASSWORD={pass};USER ID={user}");
                    break;
                case DbType.Sqlite:
                    listConn.Add($"Data Source={ip};Version=3;Password={pass};");
                    break;
            }
            return InitDataBase(listConn);
        }

        /// <summary>
        /// 初始化数据库连接
        /// </summary>
        /// <param name="listConn">连接字符串</param>
        private static SqlSugarClient InitDataBase(List<string> listConn)
        {
            var connStr = "";//主库
            var slaveConnectionConfigs = new List<SlaveConnectionConfig>();//从库集合
            for (var i = 0; i < listConn.Count; i++)
            {
                if (i == 0)
                {
                    connStr = listConn[i];//主数据库连接
                }
                else
                {
                    slaveConnectionConfigs.Add(new SlaveConnectionConfig()
                    {
                        HitRate = i * 2,
                        ConnectionString = listConn[i]
                    });
                }
            }

            //如果配置了 SlaveConnectionConfigs那就是主从模式,所有的写入删除更新都走主库，查询走从库，
            //事务内都走主库，HitRate表示权重 值越大执行的次数越高，如果想停掉哪个连接可以把HitRate设为0 
            var db = new SqlSugarClient(new ConnectionConfig()
            {
                ConnectionString = connStr,
                DbType = (DbType)dbTypeInt,
                IsAutoCloseConnection = true,
                SlaveConnectionConfigs = slaveConnectionConfigs,
                IsShardSameThread = true
            });
            db.Ado.CommandTimeOut = 30000;//设置超时时间
            db.Aop.OnLogExecuted = (sql, pars) => //SQL执行完事件
            {
                //LogHelper.WriteLog($"执行时间：{db.Ado.SqlExecutionTime.TotalMilliseconds}毫秒 \r\nSQL如下：{sql} \r\n参数：{GetParams(pars)} ", "SQL执行");
            };
            db.Aop.OnLogExecuting = (sql, pars) => //SQL执行前事件
            {
                if (db.TempItems == null) db.TempItems = new Dictionary<string, object>();
            };
            db.Aop.OnError = (exp) =>//执行SQL 错误事件
            {
                //LogHelper.WriteLog($"SQL错误:{exp.Message}\r\nSQL如下：{exp.Sql}", "SQL执行");
                throw new Exception(exp.Message);
            };
            db.Aop.OnExecutingChangeSql = (sql, pars) => //SQL执行前 可以修改SQL
            {
                return new KeyValuePair<string, SugarParameter[]>(sql, pars);
            };
            db.Aop.OnDiffLogEvent = (it) => //可以方便拿到 数据库操作前和操作后的数据变化。
            {
                //var editBeforeData = it.BeforeData;
                //var editAfterData = it.AfterData;
                //var sql = it.Sql;
                //var parameter = it.Parameters;
                //var data = it.BusinessData;
                //var time = it.Time;
                //var diffType = it.DiffType;//枚举值 insert 、update 和 delete 用来作业务区分

                //你可以在这里面写日志方法
            };
            return db;
        }
        /// <summary>
        /// 获取参数信息
        /// </summary>
        /// <param name="pars"></param>
        /// <returns></returns>
        private static string GetParams(SugarParameter[] pars)
        {
            return pars.Aggregate("", (current, p) => current + $"{p.ParameterName}:{p.Value}, ");
        }
    }
}
